Android Tips #33 TaskStackBuilder と NavUtils を使って Intent のスタックをつくる
TaskStackBuilder と NavUtils
TaskStackBuilder で合成スタックを簡単につくれる
TaskStackBuilder は Android 4.1 (APIレベル16) で導入された合成スタックをつくるユーティリティクラスです。複数の Intent をスタックしたタスクを簡単につくることができます。Support Package にも含まれているので Android 1.6 (APIレベル4) から実装することができます!
NavUtils で "Up" の画面遷移を実装できる
NavUtils は親子関係にある Activity の画面遷移を実装するためのユーティリティクラスです。NavUtils に含まれているメソッドは Android 4.1 以降であれば Activity のメソッドになっていますが、 Android 4.0 以前のバージョンで実装するためのクラスです。こちらも Support Package に含まれているので Android 1.6 から使うことができます。
"Up" という画面遷移は Android 3.0 (APIレベル11) で初めて導入されました。Android 2.3.3 以前のバージョンでは "戻る" 遷移はハードウェアキーの "Back" ボタンしかありませんでしたが、Android 3.0 からは ActionBar の "Up" ボタンで新しい形の戻る遷移ができるようになりました。
使うと便利なケース
TaskStackBuilder と NavUtils は、以下のような ルート 以外の Activity をいきなり起動したい場合に便利です。
- App Widget からアプリを起動するとき
- Notification からアプリを起動するとき
- 暗黙的 Intent からアプリを起動するとき
合成スタックをつくった上で Activity を起動することで、 Back キーを操作をしたときに戻り先の Activity に遷移することができます。また Up 遷移を使うことによって起動した Activity の親の Activity に (これまで一度も遷移したことがなくても) 遷移することもできます。
TaskStackBuilder と NavUtils の使いかた
1. Activity の親子関係を定義する
まずは Activity 同士の親子関係を AndroidManifest.xml に定義します。Up で戻る先の Activity が親の Activity です。今回は下記のような親子関係を定義します。
- GrandParentActivity
- ParentActivity (GrandParentActivity の子)
- MainActivity (ParentActivity の子)
これを AndroidManifest.xml に書くと下記のようになります。Android 3.0 以降は activity タグの android:parentActivityName 要素に、Android 2.3.3 (APIレベル10) 以前は meta-data タグに親 Activity のクラス名を定義します。
<application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <!-- MainActivity --> <activity android:name="com.example.navutilssample.MainActivity" android:label="@string/app_name" android:parentActivityName="com.example.navutilssample.ParentActivity" > <meta-data android:name="android.support.PARENT_ACTIVITY" android:value="com.example.navutilssample.ParentActivity" > </meta-data> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <!-- ParentActivity --> <activity android:name="com.example.navutilssample.ParentActivity" android:label="@string/app_name" android:parentActivityName="com.example.navutilssample.GrandParentActivity" > <meta-data android:name="android.support.PARENT_ACTIVITY" android:value="com.example.navutilssample.GrandParentActivity" > </meta-data> </activity> <!-- GrandParentActivity --> <activity android:name="com.example.navutilssample.GrandParentActivity" android:label="@string/app_name" > </activity> </application>
2. TaskStackBuilder で Intent をスタックする
次に TaskStackBuilder を使って Intent をスタックします。Android 4.1 以降は android.app.TaskStackBuilder を、Support Package を使う場合は android.support.v4.app.TaskStackBuilder を参照します。 addNextIntent() で Intent をスタックに追加していきます。追加後 startActivities() で Activity を開始します。すると最後にスタックに追加した Intent で指定した Activity が表示されます。
今回はサンプルなのでボタンを押したときに実行するような実装にしていますが、実際のタイミングは Notification がクリックされて起動したときなどになると思います。
findViewById(R.id.build_button).setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { // 複数の Intent をスタックして起動する TaskStackBuilder builder = TaskStackBuilder.create(MainActivity.this); builder.addNextIntent(new Intent(MainActivity.this, GrandParentActivity.class)); builder.addNextIntent(new Intent(MainActivity.this, ParentActivity.class)); builder.addNextIntent(new Intent(MainActivity.this, ParentActivity.class)); builder.addNextIntent(new Intent(MainActivity.this, MainActivity.class)); builder.addNextIntent(new Intent(MainActivity.this, MainActivity.class)); builder.startActivities(); finish(); } });
Android 2.3.3 以前ではスタックされない!
上記で実装した画面を Back キーで戻る操作をしてみると、下図のようになると思います。
Android 3.0 以降の場合
GrandParentActivity ← ParentActivity(2) ← ParentActivity(1) ← MainActivity(2) ← MainActivity(1)
Android 2.3.3 以前の場合
MainActivity
Android 2.3.3 以前の場合は TaskStackBuilder で積まれた Intent は無視され、最後に追加した Intent で起動した Activity だけになります。これは Back キーで前のタスクに戻ることができるようにするための仕様のようです。
じゃあ startActivity() と変わらないじゃん。と言われるとそんな気もしてきますが…少なくとも Android 2.3.3 以前のみのバージョンにだけ対応したい場合はそうかも知れません。しかし Android 3.0 以降は有効なので、これからは必要になってくるのではないでしょうか。全バージョンで同じような画面遷移にしたい場合はもう一工夫加えなければいけないと思います。
3. Up の画面遷移を実装する
次に Up の画面遷移を入れてみましょう。Up の画面遷移を実装するには Android 4.1 以降であれば Activity#navigateUpTo() メソッドを使います。Android 4.0 以前のバージョンは NavUtils#navigateUpTo() メソッドを使います。NavUtils はナビゲーションをサポートするユーティリティクラスで Support Package に含まれています。
また、親の Activity の Intent を取得するには Activity#getParentActivityIntent() を使います。Android 4.0 以前のバージョンは NavUtils#getParentActivityIntent() を使います。
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { // API レベル 16 以降 Intent upIntent = getParentActivityIntent(); navigateUpTo(upIntent); } else { // API レベル 15 以前 Intent upIntent = NavUtils.getParentActivityIntent(MainActivity.this); NavUtils.navigateUpTo(MainActivity.this, upIntent); }
上記の処理を MainActivity と ParentActivity に実装し、Up の操作をおこなうと以下のようになります。
GrandParentActivity ← ParentActivity ← MainActivity
ParentActivity と MainActivity の間に Activity がいくつもあったとしても Up は親の Activity に遷移するので、その間の Activity はクリアされます。また、スタック内に親の Activity がない場合は生成して遷移するので、確実に親の Activity に遷移できます。
まとめ
Android アプリの画面遷移は、正しく理解せずに実装するとすぐカオスになります。さらに Android 3.0 からは Up という新しい画面遷移が追加されました。さらにカオスになりそうなところですが、これはユーザーに一貫したユーザーエクスペリエンスを体験させるために生み出された概念です。Android デザインガイドラインを正しく理解し実装することで、ユーザーが「どのように操作すればどのように表示されるか」予想できるようなアプリに仕上げることができます。
最近はバージョン問わず Android 4.0 の UI・UX に合わせているアプリが増えてきています。今後は Android 4.0 に合わせた UI・UX を作っていくべきだと思います。TaskStackBuilder と NavUtils を使って Android 4.0 Ready なアプリをつくっていきましょう。
参考
- TaskStackBuilder | Android Developers
- TaskStackBuilder | Android Developers
- NavUtils | Android Developers
- Activity | Android Developers
- Implementing Ancestral Navigation | Android Developers
- Navigation with Back and Up | Android Developers
- Androidデザイン ナビゲーション | Firespeed
- Android:UPナビゲーションをカスタマイズする | Yukiの枝折